package com.javayh.oauth2.server.conf;

import com.javayh.oauth.base.BaseTokenEnhancer;
import com.javayh.oauth.base.SecurityHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.JdbcApprovalStore;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;

import javax.sql.DataSource;

/**
 * <p>
 *       认证服务配置
 * </p>
 * @version 1.0.0
 * @author Dylan-haiji
 * @since 2020/4/8
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration
		extends AuthorizationServerConfigurerAdapter {

	@Autowired(required = false)
	private AuthenticationManager authenticationManager;

	@Autowired
	private DataSource dataSource;

	@Autowired
	@Qualifier("userDetailsServiceImpl")
	private UserDetailsService userDetailsService;

	/**
	 * 自定义获取客户端,为了支持自定义权限,
	 */
	@Bean
	public ClientDetailsService clientDetails() {
		JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(
				dataSource);
		jdbcClientDetailsService.setPasswordEncoder(new BCryptPasswordEncoder());
		return jdbcClientDetailsService;
	}

	/**
	 * 令牌存放
	 * @return
	 */
	@Bean
	public TokenStore tokenStore() {
		// 基于 JDBC 实现，令牌保存到数据
		return new JdbcTokenStore(dataSource);
	}

	/**
	 * 授权store
	 * @return
	 */
	@Bean
	public ApprovalStore approvalStore() {
		return new JdbcApprovalStore(dataSource);
	}

	/**
	 * 令牌信息拓展
	 * @return
	 */
	@Bean
	public TokenEnhancer tokenEnhancer() {
		return new BaseTokenEnhancer();
	}

	/**
	 * 授权码
	 * @return
	 */
	@Bean
	public AuthorizationCodeServices authorizationCodeServices() {
		return new JdbcAuthorizationCodeServices(dataSource);
	}

	/**
	 * 配置客户端详细信息服务
	 */
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		clients.jdbc(dataSource);
		clients.withClientDetails(clientDetails());
	}

	/**
	 * 令牌访问端点
	 */
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints)
			throws Exception {
		endpoints
				// 允许post提交
				.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
				// 密码模式需要
				.authenticationManager(authenticationManager)
				.approvalStore(approvalStore()).userDetailsService(userDetailsService)
				.tokenServices(createDefaultTokenServices())
				.accessTokenConverter(SecurityHelper.buildAccessTokenConverter())
				// 设置令牌存储在数据库
				.tokenStore(tokenStore())
				.authorizationCodeServices(authorizationCodeServices());
	}

	private DefaultTokenServices createDefaultTokenServices() {
		DefaultTokenServices tokenServices = new DefaultTokenServices();
		// 令牌存储策略
		tokenServices.setTokenStore(tokenStore());
		tokenServices.setTokenEnhancer(tokenEnhancer());
		// 是否产生刷新令牌
		tokenServices.setSupportRefreshToken(true);
		tokenServices.setReuseRefreshToken(true);
		tokenServices.setClientDetailsService(clientDetails());
		return tokenServices;
	}

	/**
	 * 令牌访问端点安全策略
	 */
	@Override
	public void configure(AuthorizationServerSecurityConfigurer security)
			throws Exception {
		security
				// 开启/oauth/check_token验证端口认证权限访问
				.checkTokenAccess("isAuthenticated()")
				// 开启表单认证，申请令牌
				.allowFormAuthenticationForClients()
				// 允许表单验证
				.checkTokenAccess("permitAll()");
	}

}
